/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package futbol.tacticas.concursantes.Potrosa;

import java.awt.Point;

/**
 *
 * @author IGadea
 */
public class Entrenador {

    private Observador observador = Observador.instancia();
    private Observador.MejorDestino interseccion[] = new Observador.MejorDestino[11];

    private int posiblesSuperAtacantes[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    private boolean establecidosSuperAtacantes = false;
    private int sinSuperAtacantes = 0;
    private int superAtacante1 = -1;
    private int superAtacante2 = -1;
    
    private Configuracion config = new Configuracion();

    private double distanciaPresionPortero = 1000;
    private int contadorPresion = 0;
    
    public class Configuracion {
        public Configuracion() {
        }
        
        public Configuracion(Configuracion conf) {
            manejarPortero = conf.manejarPortero;
            antiMarcajes = conf.antiMarcajes;
            contraSuperAtacantes = conf.contraSuperAtacantes;
            desmarcarJugadores = conf.desmarcarJugadores;
            marcarRivales = conf.marcarRivales;
            despejarBalon = conf.despejarBalon;
            intersectarBalon = conf.intersectarBalon;
            evitarBucles = conf.evitarBucles;
            priorizarPasesLargos = conf.priorizarPasesLargos;
            siempreDefiende = conf.siempreDefiende;
        }

        public void configurar(String cfg) {
            if (cfg.length() == 10) {
                manejarPortero       = (cfg.charAt(0) == 'T'); //0
                antiMarcajes         = (cfg.charAt(1) == 'T'); //1
                contraSuperAtacantes = (cfg.charAt(2) == 'T'); //2
                desmarcarJugadores   = (cfg.charAt(3) == 'T'); //3
                marcarRivales        = (cfg.charAt(4) == 'T'); //4
                despejarBalon        = (cfg.charAt(5) == 'T'); //5
                intersectarBalon     = (cfg.charAt(6) == 'T'); //6
                evitarBucles         = (cfg.charAt(7) == 'T'); //7
                priorizarPasesLargos = (cfg.charAt(8) == 'T'); //8
                siempreDefiende      = (cfg.charAt(9) == 'T'); //9
            } else {
                out("ERROR: Configuracion fallida");
            }
        }

        public boolean manejarPortero = true;
        public boolean antiMarcajes = true;
        public boolean contraSuperAtacantes = true;

        public boolean desmarcarJugadores = true;
        public boolean marcarRivales = true;
        public boolean despejarBalon = false;
        
        public boolean intersectarBalon = true;
        public boolean evitarBucles = true;
        
        public boolean priorizarPasesLargos = false;
        public boolean siempreDefiende = false;
    };

    public Entrenador() {
    }

    public void configurar(String cfg) {
        config.configurar(cfg);
    }

    public void dirigirJugadores(Jugador jugadores[]) {
        Jugador.SITUACION situacion = obtenerSituacion();
        mostrarInformacionJugada(situacion);
        iniciarJugada(jugadores, situacion);

        config.configurar("TFTTTTTTFF");//"TFTFTFTFFT");
//        JugadaStandard(jugadores, situacion, config);
        JugadaPimpampum(jugadores, situacion, config);
    }

    private void JugadaStandard(Jugador jugadores[], Jugador.SITUACION situacion, Configuracion config) {
        boolean descartes[] = { false, false, false, false, false,
                                false, false, false, false, false, false };

        if (config.antiMarcajes)
            tecnicaAntiMarcajes(jugadores, situacion);
        if (config.contraSuperAtacantes)
            manejarSuperAtacantes(jugadores, situacion, descartes);

        // Elegir la tactica en funcion de quien tiene la posicion.
        if (situacion == Jugador.SITUACION.DEFENSA) {
            if (config.marcarRivales)
                marcarRivales(jugadores, situacion, descartes);
            if (config.despejarBalon)
                despejarBalon(jugadores, situacion);
        } else {
            if (config.siempreDefiende)
                marcarRivales(jugadores, situacion, descartes);
            if (config.desmarcarJugadores)
                desmarcarJugadores(jugadores, situacion);
        }

        lanzarBalon(jugadores, situacion);

        if (config.manejarPortero)
            manejarPortero(jugadores, situacion);

        if (config.intersectarBalon)
            intersectarBalon(jugadores, situacion);
        if (config.evitarBucles)
            evitarBucles(jugadores, situacion);
    }

    private void JugadaPimpampum(Jugador jugadores[], Jugador.SITUACION situacion, Configuracion config) {
        double ANGULOX = 0.08 * Math.PI;
        MathVector mv;

        // En general actuar del modo standard.
        Configuracion configEspecial = new Configuracion(this.config);
        configEspecial.intersectarBalon = false;
        configEspecial.evitarBucles = true;
        JugadaStandard(jugadores, situacion, configEspecial);

        boolean posicionados = pimpampumPosicionados(jugadores);
        if (!posicionados) {
            // Acumular jugadores en el area rival.
            mv = new MathVector(-2*ANGULOX, 126);
            mv.desplazarVector(-36, 0);
            out("PIMPAMPUM: Posicionando jugadores");
        } else {
            // Orientar a los atacantes.
            mv = new MathVector(observador.posicionBalon(), jugadores[9].posicionActual());
            mv.girarAngulo(-2*ANGULOX);
            mv.cambiarTamanyo(126);
            out("PIMPAMPUM: Orientando jugadores");
        }

        // Situar a los atacantes en su posicion correcta.
        for (int j=6; j<11; j++) {
            Point donde = new Point((int)mv.destinoX(), (int)mv.destinoY());
            jugadores[j].asignarTarea(Jugador.TAREA.DESPLAZARSE, situacion, 0, donde);
            mv.girarAngulo(ANGULOX);
        }

        // Realizar el pase si es posible.
        if (/*posicionados &&*/ pimpampumPasePosible()) {
            int jugador = observador.jugadorMasCercano();
            while (observador.distanciaJugador(jugador) < 11) {
                MathVector pase = new MathVector(jugadores[jugador].posicionActual(), 
                                                 jugadores[9].posicionActual());
                Point donde = observador.puntoDistante(pase, 1000, false);
                jugadores[jugador].golpearBalon(donde);
                observador.avisarPase(jugadores[jugador].fuerza()*3);  // Deberia ser fuerza 7 !!
                out("PIMPAMPUM: Pase al 9 del jugador " + (jugador+1));
                jugador = observador.siguienteJugador();
            }
        }

        if (config.intersectarBalon)
            intersectarBalon(jugadores, situacion);
    }

    private boolean pimpampumPasePosible() {
        double distancia9 = observador.distanciaJugador(8);
        out("PIMPAMPUM: distancia balon a 9 = " + distancia9);
        return Math.abs(distancia9 - 126) < 15;
    }
    
    private boolean pimpampumPosicionados(Jugador jugadores[]) {
        boolean resultado = true;
        for (int j=6; j<11; j++) {
            if (Utilidades.distanciaPuntos(jugadores[j].posicionActual(), new Point(160,0)) > 126) {
                resultado = false;
                break;
            }
            if (j < 10) {
                if (Utilidades.distanciaPuntos(jugadores[j].posicionActual(), 
                                               jugadores[j+1].posicionActual()) > 35) {
                    resultado = false;
                    break;
                }
            }
        }
        return resultado;
    }

    private void mostrarInformacionJugada(Jugador.SITUACION situacion) {
        int tdj = observador.tiempoDeJuego();
        int masCercano = observador.jugadorMasCercano();
        out("---------------------------");
        out("Nuevo turno: " + tdj);
        out("Balon en (" + observador.posicionBalon().x + "," + observador.posicionBalon().y + ") a " + observador.velocidadBalon());
        int rival = observador.rivalMasCercano();
        out("Jugador mas cercano " + (masCercano+1) + " (d=" + observador.distanciaJugador(masCercano) + ") -- Rival mas cercano " + (rival+1) + " (d=" + observador.distanciaRival(observador.posicionBalon(), rival) + ")");
        if (situacion == Jugador.SITUACION.ATAQUE)
            out("Atacando!");
        else
            out("Defendiendo!");
    }

    // Regresar a las posiciones originales
    private void iniciarJugada(Jugador jugadores[], Jugador.SITUACION situacion) {
        for (int n=0; n<11; n++)
            jugadores[n].asignarTarea(Jugador.TAREA.ESPERAR, situacion, 0, null);
    }

    private Jugador.SITUACION obtenerSituacion() {
        if (observador.atacando())
            return Jugador.SITUACION.ATAQUE;
        else
            return Jugador.SITUACION.DEFENSA;
    }

    private int elegirJugadorInterseccion(Jugador jugadores[]) {
        for (int n=0; n<11; n++)
            interseccion[n] = jugadores[n].interceptarBalon();

        double probMax = 0.0;
        for (int n=0; n<11; n++)
            if (interseccion[n].probabilidad > probMax)
                probMax = interseccion[n].probabilidad;

        int elegidos[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
        int contador = 0;
        for (int n=0; n<11; n++)
            if (Math.abs(interseccion[n].probabilidad - probMax) < 0.001)
                elegidos[contador++] = n;

        if (contador == 0) {
            int todos[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
            contador = 11;
            elegidos = todos;
        }

        int elegido = 10;
        int turnosMin = 100;
        for (int n=0; n<contador; n++) {
            if (interseccion[elegidos[n]].turnos < turnosMin) {
                elegido = elegidos[n];
                turnosMin = interseccion[elegido].turnos;
            }
        }
        
        return elegido;
    }
    
    private void manejarPortero(Jugador jugadores[], Jugador.SITUACION situacion) {
        int portero = buscarPortero(jugadores);
        if (porteroMalSituado(jugadores[portero]))
            asignarNuevoPortero(portero, jugadores);
        else {
            Point mover = moverPortero();
            jugadores[portero].asignarTarea(Jugador.TAREA.DESPLAZARSE, situacion, 0, mover);
        }
        
        if (porteroPresionado(jugadores[portero])) {
            boolean anteriorValor = config.priorizarPasesLargos;
            config.priorizarPasesLargos = true;
            lanzarBalon(jugadores, situacion);
            config.priorizarPasesLargos = anteriorValor;
        }
    }

    private boolean porteroPresionado(Jugador portero) {
        boolean porteroPresionado = false;

        if (observador.distanciaJugador(portero.numero()) < 11) {
            double distanciaRival = observador.distanciaRival(portero, observador.rivalMasCercano());
            if (distanciaRival < 50) {
                if (distanciaRival < distanciaPresionPortero) {
                    distanciaPresionPortero = distanciaRival;
                    if ((++contadorPresion > 5) || (distanciaRival < 20))
                        porteroPresionado = true;
                } else {
                    contadorPresion--;
                }
            } else {
                distanciaPresionPortero = 1000;
                contadorPresion = 0;
            }
        } else {
            distanciaPresionPortero = 1000;
            contadorPresion = 0;
        }
        return porteroPresionado;
    }

    // Acciones frente a marcajes ferreos.
    private void tecnicaAntiMarcajes(Jugador jugadores[], Jugador.SITUACION situacion) {
        boolean marcador[] = { false, false, false, false, false,
                               false, false, false, false, false, false };
        for (int n=0; n<11; n++)
            if (jugadores[n].estaMarcado(marcador))
                jugadores[n].asignarTarea(Jugador.TAREA.DESPLAZARSE, situacion, 0, new Point(160,0));
    }

    // Detectar superatacantes y actuar frente a ellos.
    private void manejarSuperAtacantes(Jugador jugadores[], Jugador.SITUACION situacion,
                                       boolean descartes[]) {
        detectarSuperAtacantes();

        if (superAtacante1 != -1) {
            int marcador1 = buscarMejorMarcador(jugadores, superAtacante1, descartes);
            Point desvio1 = new Point(0, 0);
            jugadores[marcador1].asignarTarea(Jugador.TAREA.MARCAR, situacion, superAtacante1, desvio1);
            descartes[marcador1] = true;

            Point desvio2;
            if (observador.distanciaRival(jugadores[marcador1], superAtacante1) > 50)
                desvio2 = new Point(0, 0);
            else
                desvio2 = new Point(20, 20);

            int marcador2 = buscarMejorMarcador(jugadores, superAtacante1, descartes);
            jugadores[marcador2].asignarTarea(Jugador.TAREA.MARCAR, situacion, superAtacante1, desvio2);
            descartes[marcador2] = true;

            out("Superatacante " + (superAtacante1+1) + " marcando por " + (marcador1+1) + " y " + (marcador2+1));
        }

        if (superAtacante2 != -1) {
            int marcador1 = buscarMejorMarcador(jugadores, superAtacante2, descartes);
            Point desvio1 = new Point(0, 0);
            jugadores[marcador1].asignarTarea(Jugador.TAREA.MARCAR, situacion, superAtacante2, desvio1);
            descartes[marcador1] = true;

            Point desvio2;
            if (observador.distanciaRival(jugadores[marcador1], superAtacante2) > 50)
                desvio2 = new Point(0, 0);
            else
                desvio2 = new Point(20, 20);

            int marcador2 = buscarMejorMarcador(jugadores, superAtacante2, descartes);
            jugadores[marcador2].asignarTarea(Jugador.TAREA.MARCAR, situacion, superAtacante2, desvio2);
            descartes[marcador2] = true;

            out("Superatacante " + (superAtacante2+1) + " marcando por " + (marcador1+1) + " y " + (marcador2+1));
        }
    }

    private void marcarRivales(Jugador jugadores[], Jugador.SITUACION situacion, boolean descartes[]) {
        // Encontrar que rivales estan en mi area de juego y que jugadores les marcaran.
        int atacantesRivales[] = new int[11];
        int rivalesAtacando = observador.atacantesRivales(atacantesRivales);

        // Actuar frente a los atacantes normales.
        for (int i=0; i<rivalesAtacando; i++) {
            if ((atacantesRivales[i] != superAtacante1) && (atacantesRivales[i] != superAtacante2)) {
                int marcador = buscarMejorMarcador(jugadores, atacantesRivales[i], descartes);
                if (marcador != -1) {
                    jugadores[marcador].asignarTarea(Jugador.TAREA.MARCAR, situacion, atacantesRivales[i], null);
                    out("Jugador " + (marcador+1) + " marcando a " + (atacantesRivales[i]+1));
                    descartes[marcador] = true;
                } else {
                    out("Rival " + (atacantesRivales[i]+1) + " no puede ser marcado");
                }
            }
        }
    }            

    private void lanzarBalon(Jugador jugadores[], Jugador.SITUACION situacion) {
        int jugador = observador.jugadorMasCercano();
        while (observador.distanciaJugador(jugador) < 11) {
            int pasesLargos = 0;
            if (config.priorizarPasesLargos)
                pasesLargos = 1;

            // Si hay alguna opcion de pase, realizarlo, sino tirar a puerta.
            double probPase = jugadores[jugador].asignarTarea(Jugador.TAREA.PASAR, situacion, pasesLargos, null);
            out("Pase de " + (jugador+1) + " con probabilidad " + probPase);
            if (probPase < 0.60) {
                jugadores[jugador].tirarAPuerta();
                out("Lanzar el balon por miedo");
            }

            // En cualquier caso, si hay suficiente probabilidad de gol, tirar.
            int fuerza = jugadores[jugador].fuerza();
            double probTiro = observador.probabilidadDeMarcar(observador.posicionBalon(), fuerza, true);
            out("Jugador " + (jugador+1) + " tiene una probabilidad de marcar de " + probTiro);
            if ((probTiro > probPase) || (probTiro > 0.70))
                jugadores[jugador].asignarTarea(Jugador.TAREA.CHUTAR, situacion, 0, null);

            jugador = observador.siguienteJugador();
        }
    }

    private void desmarcarJugadores(Jugador jugadores[], Jugador.SITUACION situacion) {
        // Pedir a los jugadores que se desmarquen.
        for (int n=0; n<11; n++)
            jugadores[n].asignarTarea(Jugador.TAREA.DESMARCARSE, situacion, 0, null);
    }

    private void despejarBalon(Jugador jugadores[], Jugador.SITUACION situacion) {
        int jugador = observador.jugadorMasCercano();
        while (observador.distanciaJugador(jugador) < 11) {
            jugadores[jugador].tirarAPuerta();
            out("Disparo a puerta de " + (jugador+1));
            jugador = observador.siguienteJugador();
        }
    }

    // Enviar al jugador mas aventajado a recuperar el balon.
    private void intersectarBalon(Jugador jugadores[], Jugador.SITUACION situacion) {
        int intersectador = elegirJugadorInterseccion(jugadores);
        Point lugar = interseccion[intersectador].interseccion;
        jugadores[intersectador].asignarTarea(Jugador.TAREA.DESPLAZARSE, situacion, 0, lugar);
        out("Elegido el jugador " + (intersectador+1) + " para recupear el balon");
    }

    // Si llevamos demasiado tiempo con la posesion, lanzar el balon (bucle infinito?)
    private void evitarBucles(Jugador jugadores[], Jugador.SITUACION situacion) {
        if ((observador.tiempoPosesion() > 90) && (observador.resultadoPartido() <= 1))
            despejarBalon(jugadores, situacion);
    }

    // Funcion con la que los jugadores informan al enrenador de a que "bote" pueden llegar.
    public void puedeLlegar(Observador.MejorDestino md) {
        if (md.numero != -1)
            interseccion[md.numero] = md;
    }

    private int buscarMejorMarcador(Jugador jugadores[], int rival, boolean jugadoresDescartados[]) {
        double maxAdap = 0;
        int marcador = -1;
        for (int j=0; j<11; j++) {
            if (!jugadoresDescartados[j]) {
                double adap = jugadores[j].posibilidadMarcaje(rival);
                if (adap > maxAdap) {
                    maxAdap = adap;
                    marcador = j;
                }
            }
        }
        return marcador;
    }

    private void detectarSuperAtacantes() {
        if (!establecidosSuperAtacantes) {
            int candidatos[] = new int[11];
            int contador = 0;

            double media = observador.mediaFuerzaRivales();
            if (media < 6) {
                for (int n=0; n<11; n++) {
                    if ((n != superAtacante1) && (n != superAtacante2)) {
                        if (observador.fuerzaRival(n) == 7) {
                            double prob = observador.probabilidadDeMarcar(observador.posicionRival(n), 
                                                                    observador.fuerzaRival(n), false);
                            if (prob > 0.75)
                                candidatos[contador++] = n;
                        }
                    }
                }
            }

            for (int i=0; i<11; i++)
                posiblesSuperAtacantes[i]--;

            int n1 = candidatos[0];
            int n2 = candidatos[1];
            if (contador == 1) {
                posiblesSuperAtacantes[n1] += 2;
                if (posiblesSuperAtacantes[n1] > 3) {
                    if (superAtacante1 == -1)
                        superAtacante1 = n1;
                    else if (superAtacante2 == -1)
                        superAtacante2 = n1;
                    posiblesSuperAtacantes[n1] = -2000;
                }
            } else if (contador == 2) {
                posiblesSuperAtacantes[n1] += 2;
                posiblesSuperAtacantes[n2] += 2;
                if (posiblesSuperAtacantes[n1] > 3) {
                    if (superAtacante1 == -1)
                        superAtacante1 = n1;
                    else if (superAtacante2 == -1)
                        superAtacante2 = n1;
                    posiblesSuperAtacantes[n1] = -2000;
                }
                if (posiblesSuperAtacantes[n2] > 3) {
                    if (superAtacante1 == -1)
                        superAtacante1 = n2;
                    else if (superAtacante2 == -1)
                        superAtacante2 = n2;
                    posiblesSuperAtacantes[n2] = -2000;
                }
            } else if (contador > 2) {
                superAtacante1 = -1;
                superAtacante2 = -1;
                establecidosSuperAtacantes = true;
            } else {
                if (++sinSuperAtacantes > 100)
                    establecidosSuperAtacantes = true;
            }

            if ((superAtacante1 != -1) && (superAtacante2 != -1))
                establecidosSuperAtacantes = true;
        }
    }

    // Solo soporta un unico portero!!
    private int buscarPortero(Jugador jugadores[]) {
        for (int n=0; n<11; n++)
            if (jugadores[n].rol() == Jugador.TIPO.GUARDAMETA)
                return n;
        return 0;
    }

    private boolean porteroMalSituado(Jugador portero) {
        return Utilidades.distanciaPuntos(portero.posicionActual(), new Point(-160, 0)) > 70;
    }

    // Al jugador mas cerca de la meta propia se le asigna el rol de portero y se quita ese rol al portero.
    private void asignarNuevoPortero(int portero, Jugador jugadores[]) {
        // Buscar al jugador mas cercano a puerta.
        int jugador = 0;
        double masCerca = 1000;
        for (int n=0; n<11; n++) {
            double distancia = Utilidades.distanciaPuntos(jugadores[n].posicionActual(), new Point(-160,0));
            if (distancia < masCerca) {
                masCerca = distancia;
                jugador = n;
            }
        }

        // Intercambiar jugador por portero.
        if ((portero != -1) && (jugador != -1)) {
            jugadores[portero].cambiarRol(jugadores[jugador].rol());
            jugadores[jugador].cambiarRol(Jugador.TIPO.GUARDAMETA);

            Point baseDefensa = jugadores[jugador].posicionBase(Jugador.SITUACION.DEFENSA);
            Point baseAtaque = jugadores[jugador].posicionBase(Jugador.SITUACION.ATAQUE);
            jugadores[jugador].alineacion(jugadores[portero].posicionBase(Jugador.SITUACION.DEFENSA),
                                          jugadores[portero].posicionBase(Jugador.SITUACION.ATAQUE));
            jugadores[portero].alineacion(baseDefensa, baseAtaque);
        }
    }

    private Point moverPortero() {
        MathVector mv = new MathVector(observador.posicionBalon(), new Point(-160, 0));
        return observador.puntoDistante(mv, (int)mv.tamanyo() - 25, false);
    }

    private void out(String str) {
//        System.out.println(str);
    }
}
